@file:Suppress("SpellCheckingInspection")

package han.cirno.corrupt.components.school.impl.fafu

import han.cirno.corrupt.data.instances.UserClassesData
import han.cirno.corrupt.components.classes.ToggleList
import han.cirno.corrupt.util.RegexUtil
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.TextNode
import kotlin.math.max

object FafuClassResolver {
    fun analyse(htmlText:String):UserClassesData.ClassTable{
        val document = Jsoup.parse(htmlText)
        val table = document.getElementById("Table1")?:throw Exception("Table1 not found: $htmlText")
        val children = table.children()[0].children()
        val classTable = UserClassesData.ClassTable(
            findToggleList(document,"xnd")!!.getSelected(),
            findToggleList(document,"xqd")!!.getSelected())
        var dayArray= intArrayOf(0,0,0,0,0,0,0)
        var lastDayArray: IntArray
        children.withIndex().forEach { (i,child)->
            if(i<2)return@forEach
            val classRow=i-2
            var foundHead=false
            var dayIndex=0
            dayArray.withIndex().forEach{(i,v)->
                dayArray[i]= max(0,v-1)
            }
            lastDayArray= dayArray.copyOf()
            child.children().forEach Inner@{
                if(RegexUtil.matchFirst("(第).*?(节)",it.text())!=null&&!foundHead){
                    foundHead=true
                    return@Inner
                }
                if (!foundHead)return@Inner
                var classSpan = it.attribute("rowspan")?.value?.toInt()
                if(classSpan==null)classSpan=1
                classTable.classEntries.addAll(genEntries(classTable.year,classTable.term,it.textNodes(),classRow,classSpan,dayIndex,dayArray,lastDayArray))
                dayIndex++
            }
            dayArray= lastDayArray.copyOf()
        }
        mergeSames(classTable)
        return classTable
    }

    private fun findDay(dayArray: IntArray, dayIndex: Int):Int{
        var p=-1
        dayArray.withIndex().forEach{(i,v)->
            if(v==0)p++
            if(p==dayIndex)return i
        }
        return -1
    }

    private fun genEntries(
        year:String,
        term:String,
        content: MutableList<TextNode>,
        classRow:Int, classSpan:Int,
        dayIndex:Int,
        dayArray:IntArray,
        lastDayArray:IntArray):MutableList<UserClassesData.ClassEntry>{
        val strings:ArrayList<String> =ArrayList()
        content.forEach { strings.add(it.text()) }
        val output:MutableList<UserClassesData.ClassEntry> =ArrayList()
        strings.withIndex().forEach {(i,s)->
            val weeks = RegexUtil.matchFirst("(?<=\\{).*?(?=\\})", s) ?: return@forEach
            val name=strings[i-1]
            val teacher=strings[i+1]
            val place=if(i+2<strings.size)strings[i+2] else ""
            output.add(UserClassesData.ClassEntry("$year:$term",name,teacher,
                UserClassesData.DayOfWeek.valueOf(findDay(dayArray,dayIndex))!!,
                getWeeks(weeks),
                UserClassesData.TimeRange(classRow, classSpan + classRow - 1),place))
            lastDayArray[findDay(dayArray,dayIndex)]=classSpan
        }
        return output
    }

    private fun getWeeks(weeks:String):IntArray{
        val weeksSplit = weeks.split("|")
        val weeksList:MutableList<Int> =ArrayList()
        weeksSplit.forEach {
            val rangeMatch = RegexUtil.matchFirst("(?<=第).*?(?=周)", it)
            if(rangeMatch!=null){
                val fromToWeeks = rangeMatch.split("-")
                weeksList.addAll(IntRange(fromToWeeks[0].toInt(),fromToWeeks[1].toInt()))
            }
            val iterator = weeksList.iterator()
            if(it=="单周")
                for (week in iterator)
                    if(week%2==0)iterator.remove()
            if (it=="双周")
                for (week in iterator)
                    if(week%2!=0)iterator.remove()
        }
        return weeksList.toIntArray()
    }

    private fun findToggleList(document: Document, id:String): ToggleList<String>?{
        val element = document.getElementById(id)?:return null
        val list:MutableList<String> = ArrayList()
        var selectedIndex=-1
        element.children().withIndex().forEach {(i,it)->
            val value= it.attribute("value")?.value ?: return@forEach
            list.add(value)
            if(it.attribute("selected")?.value=="selected")selectedIndex=i
        }
        return ToggleList(list,selectedIndex)
    }

    private fun mergeSames(classTable: UserClassesData.ClassTable){
        val classEntries = classTable.classEntries
        val iterator1 = classEntries.iterator()
        iterator1.withIndex().forEach {(i,value1)->
            val iterator2 = classEntries.drop(i + 1).iterator()
            iterator2.forEach {value2->
                if (value1.name == value2.name &&
                    value1.weeks.contentEquals(value2.weeks) &&
                    value1.place == value2.place &&
                    value1.dow == value2.dow &&
                    value1.teachers == value2.teachers) {
                    if(continueslyTime(value1,value2))
                        value2.time= mergeTime(value1,value2)
                    iterator1.remove()
                }
            }
        }
    }

    private fun continueslyTime(classEntry1: UserClassesData.ClassEntry,classEntry2: UserClassesData.ClassEntry):Boolean{
        return classEntry1.time.to+1==classEntry2.time.from||
                classEntry1.time.from==classEntry2.time.to+1
    }

    private fun mergeTime(classEntry1: UserClassesData.ClassEntry,classEntry2: UserClassesData.ClassEntry): UserClassesData.TimeRange {
        return if(classEntry1.time.from<=classEntry2.time.from)
            UserClassesData.TimeRange(classEntry1.time.from, classEntry2.time.to)
        else
            UserClassesData.TimeRange(classEntry2.time.from, classEntry1.time.to)
    }
}